package com.makemoney.miniprogram.security.config;

import com.makemoney.miniprogram.security.component.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * Spring Security 配置类
 */
@Configuration
@EnableWebSecurity
@Slf4j
public class SecurityConfig {

    // Spring Security 白名单资源路径配置
    private final IgnoreUrlsConfig ignoreUrlsConfig;

    // 自定义返回结果：没有权限访问时
    private final RestfulAccessDeniedHandler restfulAccessDeniedHandler;

    // 自定义返回结果：没有登录或 token 过期时
    private final RestAuthenticationEntryPoint restAuthenticationEntryPoint;

    // JWT 拦截器
    private final JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    // 构造函数注入
    public SecurityConfig(IgnoreUrlsConfig ignoreUrlsConfig, RestfulAccessDeniedHandler restfulAccessDeniedHandler, RestAuthenticationEntryPoint restAuthenticationEntryPoint, JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter) {
        this.ignoreUrlsConfig = ignoreUrlsConfig;
        this.restfulAccessDeniedHandler = restfulAccessDeniedHandler;
        this.restAuthenticationEntryPoint = restAuthenticationEntryPoint;
        this.jwtAuthenticationTokenFilter = jwtAuthenticationTokenFilter;
    }

    /**
     * 配置 Spring Security 的过滤链
     * @param httpSecurity HttpSecurity
     * @return SecurityFilterChain
     * @throws Exception 异常
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        // 开始配置 URL 的授权规则
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();

        // 遍历白名单，将白名单中的 URL 配置为完全公开，不需要任何权限
        for (String url : ignoreUrlsConfig.getUrls()) {
            registry.antMatchers(url).permitAll();
        }

        // 允许所有 OPTIONS 请求（通常用于 CORS 预检请求）
        registry.antMatchers(HttpMethod.OPTIONS).permitAll();

        // 配置其他所有请求都需要认证
        registry.and().authorizeRequests()
                .anyRequest().authenticated()
                .and()
                // 禁用 CSRF 保护（在使用 token 时通常不需要）
                .csrf().disable()
                // 配置 session 策略为无状态，即不创建 session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                // 配置异常处理器
                .exceptionHandling()
                // 当访问被拒绝时使用自定义的处理器返回响应
                .accessDeniedHandler(restfulAccessDeniedHandler)
                // 当未认证或 token 过期时使用自定义的处理器返回响应
                .authenticationEntryPoint(restAuthenticationEntryPoint)
                .and()
                // 在 UsernamePasswordAuthenticationFilter 之前添加 JWT 拦截器
                .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

        return httpSecurity.build();
    }
}

